package ru.hei.pointsserver.repository

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.dao.DataIntegrityViolationException
import reactor.test.StepVerifier.create
import ru.hei.pointsserver.entities.House
import ru.hei.pointsserver.entities.PointsLog
import ru.hei.pointsserver.entities.Professor
import java.sql.Timestamp
import java.time.Instant
import java.util.*


class PointsLogRepositoryTest @Autowired constructor(
  private val housesRepository: HousesRepository,
  private val professorRepository: ProfessorRepository,
  private val subject: PointsLogRepository
) : BaseRepositoryTest() {

  @AfterEach
  fun clear() {
    create(
      subject.deleteAll()
        .then(professorRepository.deleteAll())
        .then(housesRepository.deleteAll())
    )
      .expectComplete()
      .verify()
  }

  @Test
  fun `Should save log`() {
    // Given
    val professorToSave = Professor(name = "test")
    val houseToSave = House(name = "test", points = 10)

    // When
    // Then
    create(
      professorRepository.save(professorToSave)
        .flatMap { professor ->
          housesRepository.save(houseToSave)
            .map { professor to it }
        }
        .map { (professor, house) ->
          PointsLog(
            professorId = professor.id!!,
            points = house.points,
            houseId = house.id!!,
          )
        }
        .flatMap { expectedLog -> subject.save(expectedLog).map { expectedLog to it } }
    )
      .expectSubscription()
      .assertNext { (expectedLog, log) ->
        assertThat(log.id).isNotNull()
        assertThat(log.time).isNotNull()
        assertThat(log.time).isBefore(Timestamp.from(Instant.now().plusSeconds(1)))
        assertThat(log.professorId).isEqualTo(expectedLog.professorId)
        assertThat(log.points).isEqualTo(expectedLog.points)
        assertThat(log.houseId).isEqualTo(expectedLog.houseId)
      }
      .expectComplete()
      .verify()
  }

  @Test
  fun `Should fail when professor not exists`() {
    // Given
    val house = House(name = "test", points = 10)

    // When
    // Then
    create(housesRepository.save(house)
      .map { PointsLog(professorId = UUID.randomUUID(), points = it.points, houseId = it.id!!) }
      .flatMap { subject.save(it) })
      .expectSubscription()
      .expectError(DataIntegrityViolationException::class.java)
      .log()
      .verify()
  }

  @Test
  fun `Should fail when house not exists`() {
    // Given
    val professor = Professor(name = "test")

    // When
    // Then
    create(professorRepository.save(professor)
      .map { PointsLog(professorId = it.id!!, points = 10, houseId = 666) }
      .flatMap { subject.save(it) })
      .expectSubscription()
      .expectError(DataIntegrityViolationException::class.java)
      .log()
      .verify()
  }
}